﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace LightCAD.Core.Elements
{
    public class LcComposite:LcElement, IElementSet
    {
        [JsonInclude]
        public ElementCollection Elements { get; } = new ElementCollection();

        public override LcElement Clone()
        {
            var clone = this.Document.CreateObject<LcComposite>();
            clone.Copy(this);
            return clone;
        }
        public override void Copy(LcElement src)
        {
            base.Copy(src);
            this.Elements.Clear();
            foreach (var ele in this.Elements)
            {
                var cloneEle = ele.Clone();
                this.Elements.Add(cloneEle);
            }
        }
        public void InsertElement(LcElement element)
        {
            element.Parent = this;
            this.ResetBoundingBox();
            if (!IsFireChangedEvent)
            {
                this.Elements.Add(element);
            }
            else
            {
                var args = new ObjectChangedEventArgs
                {
                    Type = ObjectChangeType.Insert,
                    Target = element
                };
                element.OnObjectChangedBefore(args);
                if (args.IsCancel) return;

                this.Document.OnObjectChangedBefore(args);
                if (args.IsCancel) return;

                element.Document = this.Document;
                this.Elements.Add(element);

                element.OnObjectChangedAfter(args);
                this.Document.OnObjectChangedAfter(args);
            }

        }
        public bool RemoveElement(LcElement element)
        {
            if (element == null || this != element.Parent)
            {
                return false;
                throw new ArgumentException(SR.ElementNotInContainer, nameof(element));
            }


            if (!IsFireChangedEvent)
            {
                var removed = this.Elements.Remove(element);
                if (removed)
                {
                    this.ResetBoundingBox();
                }
                return removed;
            }
            else
            {
                if (!this.Elements.Contains(element))
                {
                    return false;
                }

                var args = new ObjectChangedEventArgs
                {
                    Type = ObjectChangeType.Remove,
                    Target = element
                };
                element.OnObjectChangedBefore(args);
                if (args.IsCancel) return false;

                this.Document.OnObjectChangedBefore(args);
                if (args.IsCancel) return false;

                this.Elements.Remove(element);
                element.Parent = null;

                element.OnObjectChangedAfter(args);
                this.Document.OnObjectChangedAfter(args);

                this.ResetBoundingBox();
                return true;
            }

        }
        public override Box2 GetBoundingBox()
        {
            var box = Box2.Empty;
            foreach (var ele in this.Elements)
            {
                box.Union(ele.GetBoundingBox());
            }
            return box;
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {

            var box = Box2.Empty;
            foreach (var ele in this.Elements)
            {
                box.Union(ele.GetBoundingBox(matrix));
            }
            return box;
        }
        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            foreach (var ele in this.Elements)
            {
                if (ele.IntersectWithBox(testPoly)) return true;
            }
            return false;
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            foreach (var ele in this.Elements)
            {
                if (!ele.IncludedByBox(testPoly)) return false;
            }
            return true;
        }
        public override void Translate(double dx, double dy)
        {
            foreach (var ele in this.Elements)
            {
                ele.Translate(dx, dy);
            }
            ResetBoundingBox();
        }
        public override void Scale(Vector2 basePoint, double scaleFactor)
        {
            foreach (var subEle in this.Elements)
            {
                subEle.Scale(basePoint, scaleFactor);
            }
            ResetBoundingBox();
        }
        public override void Rotate(Vector2 basePoint, double rotateAngle)
        {
            foreach (var subEle in this.Elements)
            {
                subEle.Rotate(basePoint, rotateAngle);
            }
            ResetBoundingBox();
        }
        public override void Mirror(Vector2 axisStart, Vector2 axisEnd)
        {
            foreach (var subEle in this.Elements)
            {
                subEle.Mirror(axisStart, axisEnd);
            }
            ResetBoundingBox();
        }
    }
}
